from flask import Blueprint, request, jsonify, g, current_app
from flask_jwt_extended import jwt_required, get_jwt_identity
from backend.decorators import admin_required
from backend.models import db, POI, User
from backend.utils import format_response, validate_api_key_and_update_quota, api_response

poi_bp = Blueprint('poi', __name__)


@poi_bp.route('', methods=['GET'])
@format_response# 使用 @format_response 处理最终响应和异常
def get_pois():
    """
    查询POI数据（多条件筛选）。
    公共可访问，但限速严重，但如果提供了有效的 API Key，则会验证并扣减配额。
    """
    g.authenticated_by = 'anonymous'  # 默认是匿名访问
    apikey_in_request = request.args.get('apikey') or request.headers.get('X-API-KEY')

    if apikey_in_request:
        # 如果提供了 API Key，尝试验证它
        api_user, error_resp = validate_api_key_and_update_quota(apikey_in_request)
        if error_resp:
            # API Key 无效或配额不足
            return error_resp  # 直接返回错误响应 (jsonify 过的 tuple)
        if api_user:
            # API Key 有效，标记认证方式
            g.authenticated_by = 'apikey'
            g.current_user = api_user  # 可以将用户信息存入 g 供后续使用
            current_app.logger.info(f"POI query authenticated via API Key for user: {api_user.username}")

    # --- 开始构建查询 ---
    query = POI.query

    # 处理查询参数
    if name := request.args.get('name'):
        query = query.filter(POI.name.ilike(f'%{name}%'))
    if province := request.args.get('province'):
        query = query.filter_by(province=province)
    if category := request.args.get('category'):
        query = query.filter_by(category=category)

    # 新增范围过滤
    if sw_lat := request.args.get('sw_lat'):
        sw_lng = request.args.get('sw_lng')
        ne_lat = request.args.get('ne_lat')
        ne_lng = request.args.get('ne_lng')
        query = query.filter(
            POI.latitude >= sw_lat,
            POI.latitude <= ne_lat,
            POI.longitude >= sw_lng,
            POI.longitude <= ne_lng
        )

    # 新增半径过滤（需要安装geoalchemy2）
    if center_lat := request.args.get('center_lat'):
        center_lng = request.args.get('center_lng')
        radius = request.args.get('radius', 5000)  # 默认5公里
        from sqlalchemy import func
        distance = func.ST_DistanceSphere(
            func.ST_MakePoint(POI.longitude, POI.latitude),
            func.ST_MakePoint(center_lng, center_lat)
        )
        query = query.filter(distance <= radius)

        # 参数验证装饰器

    def validate_coordinates(value, min_val, max_val):
        try:
            num = float(value)
            if not (min_val <= num <= max_val):
                raise ValueError
            return num
        except (ValueError, TypeError):
            return None

        # 坐标参数验证

    if request.args.get('sw_lat'):
        sw_lat = validate_coordinates(request.args.get('sw_lat'), -90, 90)
        sw_lng = validate_coordinates(request.args.get('sw_lng'), -180, 180)
        ne_lat = validate_coordinates(request.args.get('ne_lat'), -90, 90)
        ne_lng = validate_coordinates(request.args.get('ne_lng'), -180, 180)

        if not all([sw_lat, sw_lng, ne_lat, ne_lng]):
            return {"error": "Invalid coordinate parameters"}, 400

        # 半径参数验证
    if request.args.get('radius'):
        try:
            radius = int(request.args.get('radius'))
            if radius < 0 or radius > 100000:
                raise ValueError
        except ValueError:
            return {"error": "Radius must be 0-100000 meters"}, 400

    # 分页处理
    try:
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 20, type=int)  # 允许客户端指定每页数量 (设个上限)
        per_page = min(per_page, 100)  # 例如，每页最多100条
        if page <= 0: page = 1
        if per_page <= 0: per_page = 20
    except ValueError:
        return api_response(code=400, message="无效的分页参数")  # 返回错误

    pois_pagination = query.paginate(page=page, per_page=per_page, error_out=False)

    # 准备响应数据
    result_data = {
        "list": [poi.to_dict() for poi in pois_pagination.items],
        "pagination": {
            "current_page": page,
            "per_page": per_page,
            "total_items": pois_pagination.total,
            "total_pages": pois_pagination.pages
        }
    }
    # @format_response 会自动包装这个字典并返回 200 OK
    return result_data, 200

@poi_bp.route('', methods=['POST'])
@jwt_required()
@admin_required
@format_response
def create_poi():
    """创建新POI（需要管理员权限）"""
    current_user_id = get_jwt_identity()
    data = request.json

    required_fields = ['name', 'longitude', 'latitude']
    if not all(field in data for field in required_fields):
        return jsonify({"error": "缺少必要字段"}), 400

    try:
        new_poi = POI(
            name=data['name'],
            # 最好对经纬度做类型和范围验证
            longitude=float(data['longitude']),
            latitude=float(data['latitude']),
            province=data.get('province'),
            category=data.get('category'),
            image_url=data.get('image_url'),
            website=data.get('website'),
            # 记录创建者的 ID
            created_by=int(current_user_id)
        )
    except (ValueError, TypeError) as e:
         return api_response(code=400, message=f"无效的数据格式: {e}")


    try:
        db.session.add(new_poi)
        db.session.commit()
        # 返回创建成功的 POI 数据和 201 状态码
        return new_poi.to_dict(), 201
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error creating POI: {e}", exc_info=True)
        return api_response(code=500, message=f"创建 POI 时数据库出错: {e}")

# 删除poi的接口
@poi_bp.route('/<int:poi_id>', methods=['DELETE'])
@jwt_required()# 需要 JWT 登录
@admin_required# 且必须是管理员
@format_response
def delete_poi(poi_id):
    """删除指定ID的POI（仅管理员）"""
    poi = POI.query.get(poi_id)
    if not poi:
        # return jsonify({'error': 'POI not found'}), 404 # 旧方式
        return api_response(code=404, message='指定的 POI 未找到')

    try:
        db.session.delete(poi)
        db.session.commit()
        # 返回成功消息和 200 状态码 (或 204 No Content)
        return {'message': 'POI 删除成功'}, 200
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error deleting POI {poi_id}: {e}", exc_info=True)
        return api_response(code=500, message=f"删除 POI 时数据库出错: {e}")


# 编辑poi信息的接口
@poi_bp.route('/<int:poi_id>', methods=['PUT'])
@jwt_required()
@admin_required
@format_response
def update_poi(poi_id):
    """更新指定ID的POI信息（仅管理员）"""
    poi = POI.query.get(poi_id)
    if not poi:
        return api_response(code=404, message='指定的 POI 未找到')

    data = request.json
    if not data:
        return api_response(code=400, message='请求体不能为空')

    # 更新字段 (可以添加更多验证)
    try:
        for field in ['name', 'province', 'category', 'longitude', 'latitude', 'image_url', 'website']:
            if field in data:
                # 对经纬度进行验证
                if field in ['longitude', 'latitude']:
                     setattr(poi, field, float(data[field]))
                else:
                     setattr(poi, field, data[field])
    except (ValueError, TypeError) as e:
         return api_response(code=400, message=f"无效的数据格式: {e}")


    try:
        db.session.add(poi) # 更新也需要 add 到 session
        db.session.commit()
        # 返回更新后的 POI 数据和 200 状态码
        return poi.to_dict(), 200
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error updating POI {poi_id}: {e}", exc_info=True)
        return api_response(code=500, message=f"更新 POI 时数据库出错: {e}")


